home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
OS
/
ZInterruptTimer.cpp
< prev
next >
Wrap
Text File
|
1997-06-18
|
10KB
|
405 lines
/*
* File: ZInterruptTimer.cpp
* Summary: Interrupt driven timer classes
* Written by: Jesse Jones
*
* Copyright ゥ 1996-1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <2> 4/13/97 JDJ MTimeMgrTimer descends from MExitAction instead of
* using an ExitToShell patch.
* <1> 6/23/96 JDJ Created
*/
#include <ZInterruptTimer.h>
#include <SLFunctions.h>
#include <ZDebug.h>
#include <ZExceptions.h>
#include <ZPatch.h>
//---------------------------------------------------------------
//
// Types and Constants
//
//---------------------------------------------------------------
typedef MTimeMgrTimer* TimeMgrTimerPtr;
const short kMaxTimers = 100;
// ===================================================================================
// class MVBLTimer
// ===================================================================================
//---------------------------------------------------------------
//
// MVBLTimer::~MVBLTimer
//
//---------------------------------------------------------------
MVBLTimer::~MVBLTimer()
{
ASSERT(!this->TimerIsRunning()); // Otherwise OnTime can be called while the object is being destroyed.
ASSERT(!mVBLInfo.inTask); // Can't call this from within the VBL task!
if (mTimerInstalled)
this->RemoveTimer();
if (mVBLInfo.task.vblAddr != nil)
DisposeRoutineDescriptor(mVBLInfo.task.vblAddr);
}
//---------------------------------------------------------------
//
// MVBLTimer::MVBLTimer
//
// Note that the timer has to start out stopped since the vtable
// entry for OnTime is still nil.
//
// ・・ハIt seems like we could get rid of this by providing a definition
// ・・ハfor OnTime (while keeping it pure virtual), but this doesn't
// ・・ work with CW9. (Creating a base class with a definition of
// ・・ハOnTime doesn't work either).
//
//---------------------------------------------------------------
MVBLTimer::MVBLTimer(MilliSecond freq, bool running) : MBaseTimer(freq, running)
{
ASSERT(freq >= 16); // VBL can't handle anything faster.
ASSERT(!running);
mVBLInfo.myA5 = SetCurrentA5();
mVBLInfo.thisPtr = this;
mVBLInfo.vblFreq = (short) (60*freq/1000);
mVBLInfo.inTask = false;
mVBLInfo.task.qType = vType;
mVBLInfo.task.vblAddr = NewVBLProc(OnInterrupt);
mVBLInfo.task.vblCount = mVBLInfo.vblFreq;
mVBLInfo.task.vblPhase = 0;
mTimerInstalled = false;
if (this->TimerIsRunning()) // we have a definition of OnTime so this is OK
this->InstallTimer();
}
//---------------------------------------------------------------
//
// MVBLTimer::StopTimer
//
//---------------------------------------------------------------
void MVBLTimer::StopTimer()
{
this->RemoveTimer();
Inherited::StopTimer();
}
//---------------------------------------------------------------
//
// MVBLTimer::StartTimer
//
//---------------------------------------------------------------
void MVBLTimer::StartTimer()
{
Inherited::StartTimer();
this->InstallTimer();
}
//---------------------------------------------------------------
//
// MVBLTimer::SetTimerFreq
//
//---------------------------------------------------------------
void MVBLTimer::SetTimerFreq(MilliSecond freq)
{
ASSERT(freq >= 16); // VBL can't handle anything faster.
Inherited::SetTimerFreq(freq);
mVBLInfo.vblFreq = (short) (60*freq/1000);
}
//---------------------------------------------------------------
//
// MVBLTimer::InstallTimer
//
//---------------------------------------------------------------
void MVBLTimer::InstallTimer()
{
ASSERT(!mVBLInfo.inTask); // Can't call this from within the VBL task!
if (!mTimerInstalled) {
OSErr err = VInstall((QElemPtr) &mVBLInfo.task);
ThrowIfOSErr(err);
mTimerInstalled = true;
}
}
//---------------------------------------------------------------
//
// MVBLTimer::RemoveTimer
//
//---------------------------------------------------------------
void MVBLTimer::RemoveTimer()
{
if (mTimerInstalled) {
if (mVBLInfo.inTask)
mVBLInfo.task.vblCount = 0; // Can't call VRemove from within the VBL task!
else {
OSErr err = VRemove((QElemPtr) &mVBLInfo.task);
ASSERT(err == noErr);
mTimerInstalled = false;
}
}
}
//---------------------------------------------------------------
//
// MVBLTimer::OnInterrupt [static]
//
//---------------------------------------------------------------
#pragma profile off
pascal void MVBLTimer::OnInterrupt(VBLTaskPtr taskPtr) // interrupt code
{
SVBLRecord* dataPtr = reinterpret_cast<SVBLRecord*>(taskPtr);
volatile long oldA5 = SetA5(dataPtr->myA5);
SLEnterInterrupt();
SAFE_ASSERT(dataPtr != nil);
try {
// Reset the VBL so we get called again.
dataPtr->task.vblCount = dataPtr->vblFreq;
// Set a flag so we can complain if anyone does something they
// shouldn't while we're in the VBL interrupt.
SAFE_ASSERT(!dataPtr->inTask);
dataPtr->inTask = true;
SAFE_ASSERT(dataPtr->thisPtr != nil);
if (dataPtr->thisPtr->TimerIsRunning())
dataPtr->thisPtr->OnTime();
} catch (...) {
}
dataPtr->inTask = false;
SLLeaveInterrupt();
SetA5(oldA5);
}
#pragma profile reset
#pragma mark -
// ===================================================================================
// class MTimeMgrTimer
// ===================================================================================
//---------------------------------------------------------------
//
// MTimeMgrTimer::~MTimeMgrTimer
//
//---------------------------------------------------------------
MTimeMgrTimer::~MTimeMgrTimer()
{
ASSERT(!this->TimerIsRunning()); // Otherwise OnTime can be called while the object is being destroyed.
ASSERT(!mTimeMgrInfo.inTask); // Can't call this from within the time manager task!
if (mTimerInstalled)
this->RemoveTimer();
if (mTimeMgrInfo.task.tmAddr != nil)
DisposeRoutineDescriptor(mTimeMgrInfo.task.tmAddr);
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::MTimeMgrTimer
//
// Note that the timer has to start out stopped since the vtable
// entry for OnTime is still nil.
//
// ・・ハIt seems like we could get rid of this by providing a definition
// ・・ハfor OnTime (while keeping it pure virtual), but this doesn't
// ・・ work with CW9. (Creating a base class with a definition of
// ・・ハOnTime doesn't work either).
//
//---------------------------------------------------------------
MTimeMgrTimer::MTimeMgrTimer(MilliSecond freq, bool running) : MBaseTimer(freq, running)
{
ASSERT(!running);
mTimeMgrInfo.task.tmAddr = NewTimerProc(OnInterrupt);
mTimeMgrInfo.task.tmWakeUp = 0;
mTimeMgrInfo.task.tmReserved = 0;
mTimeMgrInfo.mMyA5 = SetCurrentA5();
mTimeMgrInfo.thisPtr = this;
mTimeMgrInfo.inTask = false;
mTimerInstalled = false;
if (this->TimerIsRunning())
this->InstallTimer();
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::StopTimer
//
//---------------------------------------------------------------
void MTimeMgrTimer::StopTimer()
{
this->RemoveTimer();
Inherited::StopTimer();
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::StartTimer
//
//---------------------------------------------------------------
void MTimeMgrTimer::StartTimer()
{
Inherited::StartTimer();
this->InstallTimer();
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::SetTimerFreq
//
//---------------------------------------------------------------
void MTimeMgrTimer::SetTimerFreq(MilliSecond freq)
{
Inherited::SetTimerFreq(freq);
if (mTimerInstalled) {
this->RemoveTimer();
this->InstallTimer();
}
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::InstallTimer
//
//---------------------------------------------------------------
void MTimeMgrTimer::InstallTimer()
{
ASSERT(!mTimeMgrInfo.inTask); // Can't call this from within the time manager task!
if (!mTimerInstalled) {
mTimeMgrInfo.task.tmWakeUp = 0; // Disable drift-free execution (can cause problems with small frequencies. See IM:Processes pg 3-8, 3-9)
mTimeMgrInfo.task.tmReserved = 0; // ・・ Might want to enable drift-free execution for large frequencies.
mTimerInstalled = true;
InsXTime((QElemPtr) &mTimeMgrInfo.task);
PrimeTime((QElemPtr) &mTimeMgrInfo.task, this->GetTimerFreq());
}
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::RemoveTimer
//
//---------------------------------------------------------------
void MTimeMgrTimer::RemoveTimer()
{
if (mTimerInstalled) {
RmvTime((QElemPtr) &mTimeMgrInfo.task); // Note that IM VI says that RmvTime can be called at interrupt time...
mTimerInstalled = false;
}
}
//---------------------------------------------------------------
//
// MTimeMgrTimer::OnInterrupt [static]
//
//---------------------------------------------------------------
#pragma profile off
pascal void MTimeMgrTimer::OnInterrupt(TMTask* taskPtr) // interrupt code
{
STimeMgrRecord* dataPtr = reinterpret_cast<STimeMgrRecord*>(taskPtr);
volatile long oldA5 = SetA5(dataPtr->mMyA5);
SLEnterInterrupt();
SAFE_ASSERT(dataPtr != nil);
try {
// Set a flag so we can complain if anyone does something they
// shouldn't while we're in the time manager interrupt.
SAFE_ASSERT(!dataPtr->inTask);
dataPtr->inTask = true;
SAFE_ASSERT(dataPtr->thisPtr != nil);
if (dataPtr->thisPtr->TimerIsRunning())
dataPtr->thisPtr->OnTime();
// Prime the pump so we get called again.
if (dataPtr->thisPtr->TimerIsRunning())
PrimeTime((QElemPtr) &dataPtr->task, dataPtr->thisPtr->GetTimerFreq());
} catch (...) {
}
dataPtr->inTask = false;
SLLeaveInterrupt();
SetA5(oldA5);
}
#pragma profile reset
//---------------------------------------------------------------
//
// MTimeMgrTimer::OnAbnormalExit
//
// Unlike VBL tasks, Time Manager tasks have to be removed by the
// application when it terminates (see IM Processes page 3-9).
//
//---------------------------------------------------------------
void MTimeMgrTimer::OnAbnormalExit()
{
this->RemoveTimer();
}